import type { FullModelInfo, ModelSetting, SupplierInfo } from '@/services/model/type'
import { ref, computed, readonly } from 'vue'
import { defineStore } from 'pinia'
import { t, locale } from '@/i18n'
import xModelService from '@/services/model'
import { useGlobalStore } from '@/stores/global'
import { formatBytes } from '@/utils/format'

// 全局存储
const globalStore = useGlobalStore()

type HasCheck = {
  [key: string]: boolean
}

/**
 * 模型存储管理
 *
 * @description 管理模型与提供商动态数据，提供模型拉取与切换等功能
 */
export const useModelStore = defineStore('model', () => {
  // 本地提供商列表
  const localSuppliers = ref<SupplierInfo[]>([])
  // 本地模型列表
  const localModels = ref<FullModelInfo[]>([])
  // 使用中的模型
  const inUseModel = ref<FullModelInfo>()

  // 记录提供商是否已经通过检测
  const supplierHasCheck = ref<HasCheck>({})

  // ----- Getters -----

  const readActiveSuppliers = computed<SupplierInfo[]>(() => {
    return localSuppliers.value.filter((item) => item.isActive)
  })

  const readUsableLlmModels = computed(() => {
    return localModels.value.filter(
      (item) => item.type === 'llm' && item.inUse && item.supplier?.isActive,
    )
  })

  const readUsableEmbedingModels = computed(() => {
    return localModels.value.filter(
      (item) => item.type === 'embedding' && item.inUse && item.supplier?.isActive,
    )
  })

  // ----- Actions -----

  /**
   * 初始化数据
   */
  const initModelStore = async () => {
    // 加载完整提供商列表
    localSuppliers.value = xModelService.getInstance().getSuppliersHistory()
    // 加载完整模型列表
    localModels.value = await xModelService.getInstance().getModelsHistory()
    // 加载当前模型
    inUseModel.value = await xModelService.getInstance().getActiveModel()
    // 模型检测
    changeActiveModel(inUseModel.value?.id)
  }

  /**
   * 切换真实使用模型
   *
   * @param modelId 模型ID
   */
  const changeActiveModel = async (modelId: string) => {
    if (!modelId || readUsableLlmModels.value.findIndex((item) => item.id === modelId) < 0) {
      // 模型未使用或不存在，移除
      modelId = null
    }
    xModelService.getInstance().setActiveModel(modelId)
    inUseModel.value = await xModelService.getInstance().getActiveModel()
  }

  /**
   * 提供商连接检测
   *
   * @param supplierId 提供商ID
   * @returns
   */
  const checkSupplier = async (supplierId: string) => {
    const check = await xModelService.getInstance().connectSupplierById(supplierId)
    supplierHasCheck.value[supplierId] = check
    return check
  }

  /**
   * 获取提供商详情
   *
   * @param supplierId 提供商ID
   * @returns
   */
  const getSupplierById = (supplierId: string) => {
    return xModelService.getInstance().getSupplierById(supplierId)
  }

  /**
   * 获取指定提供商的模型
   *
   * @param supplierId 提供商ID
   * @param forceRefresh 是否强制从LLM服务器获取
   * @returns
   */
  const getSupplierModels = async (
    supplierId: string,
    forceRefresh: boolean,
  ): Promise<FullModelInfo[]> => {
    if (forceRefresh || localModels.value.filter((item) => item.supplier?.id === supplierId).length <= 0) {
      // 如果当前提供商没有添加过模型，则尝试从服务器获取
      if (forceRefresh || typeof supplierHasCheck.value[supplierId] !== 'boolean') {
        // 连接测试
        supplierHasCheck.value[supplierId] = await checkSupplier(supplierId)
        if (!supplierHasCheck.value[supplierId]) {
          throw new Error(t('setting.supplier.notPassCheck'))
        }
        // 从服务器获取
        const xModelProvider = xModelService.getInstance()
        await xModelProvider.refreshModelsWithProvider(supplierId)
        // 重载数据
        localModels.value = await xModelProvider.getModelsHistory()
        inUseModel.value = await xModelProvider.getActiveModel()
        // 模型检测
        changeActiveModel(inUseModel.value?.id)
      }
    }
    return localModels.value.filter((item) => item.supplier?.id === supplierId)
  }

  /**
   * 获取当前模型上传附件提示信息
   * 当语言切换后，也需要再执行一下此方法
   *
   * @returns
   */
  const getCurrentModelAttachUploadTips = async (): Promise<{
    /** 是否支持上传 */
    allowUpload: boolean
    /** 提示文本 */
    tips: string
    /** 允许上传 accept */
    accept: string
  }> => {
    const config = await xModelService.getInstance().getModelAttachConfig(inUseModel.value)
    if (config.useUpload) {
      let tips = ''
      if (config.allowImage && config.allowDoc) {
        // 同时允许图片和文档
        tips = t('system.attachmentUpload.fileTips', {
          docsize: formatBytes(config.docSingleLimit),
          imagesize: formatBytes(config.imagesTotalLimit),
        })
      } else if (config.allowImage) {
        // 仅图片
        tips = t('system.attachmentUpload.imageOnlyTips', {
          filesize: formatBytes(config.imagesTotalLimit),
        })
      } else if (config.allowDoc) {
        // 仅文档
        tips = t('system.attachmentUpload.docOnlyTips', {
          filesize: formatBytes(config.docSingleLimit),
        })
      }
      return { allowUpload: true, tips, accept: config.uploadAccept }
    }
    return { allowUpload: false, tips: t('system.attachmentUpload.noUploadAllowed'), accept: '' }
  }

  /**
   * 检查当前模型上传附件（抛出错误）
   *
   * @param file 上传的附件
   *
   * @returns
   */
  const checkCurrentModelAttach = async (file: File): Promise<boolean> => {
    // 当前队列
    const attachs = globalStore.getCurrentAttachs()
    if (attachs.some((f) => f.name === file.name && f.size === file.size)) {
      throw new Error(t('system.attachmentUpload.duplicateFileWarning', { filename: file.name }))
    }
    const config = await xModelService.getInstance().getModelAttachConfig(inUseModel.value)
    if (!config.useUpload) {
      throw new Error(t('system.attachmentUpload.noUploadAllowed'))
    }
    if (config.isImage(file)) {
      // 验证图片
      if (!config.allowImage) {
        throw new Error(t('system.attachmentUpload.noSupportType'))
      }
      const totalSize = attachs.reduce((sum, f) => sum + f.size, 0) + file.size
      if (totalSize > config.imagesTotalLimit) {
        throw new Error(
          t('system.attachmentUpload.totalFileCountLimit', { filesize: formatBytes(totalSize) }),
        )
      }
    } else {
      // 验证文档
      if (!config.uploadAccept.includes(file.type)) {
        throw new Error(t('system.attachmentUpload.noSupportType'))
      }
      if (file.size > config.docSingleLimit) {
        throw new Error(
          t('system.attachmentUpload.singleFileSizeLimit', { filesize: formatBytes(file.size) }),
        )
      }
    }
    return true
  }

  /**
   * 修改模型的使用状态
   *
   * @param modelId 模型ID
   * @param inUse 是否使用
   */
  const setModelInUse = async (modelId: string, inUse: boolean) => {
    await xModelService.getInstance().setModelInfo(modelId, { inUse })
    // 重载模型数据
    localModels.value = await xModelService.getInstance().getModelsHistory()
    // 模型检测
    changeActiveModel(inUseModel.value?.id)
  }

  /**
   * 获取显示的提供商名称
   *
   * @param name 原始名称
   * @requires
   */
  const getRealSupplierName = (name: { [key: string]: string }) => {
    return name[locale.value] ?? name['default']
  }

  /**
   * 清空所有模型
   */
  const clearModels = async () => {
    await xModelService.getInstance().clearAllModels()
    await changeActiveModel(null)
  }

  /**
   * 读取模型设置
   */
  const getModelSetting = () => {
    return xModelService.getInstance().readModelSetting()
  }

  /**
   * 保存模型设置
   *
   * @param setting
   */
  const setModelSetting = (setting: ModelSetting) => {
    xModelService.getInstance().saveModelSetting(setting)
  }

  return {
    /** getter: 读取已激活的提供商列表 */
    readActiveSuppliers,
    /** getter: 读取可用的llm模型列表 */
    readUsableLlmModels,
    /** getter: 读取可用的embedding模型列表 */
    readUsableEmbedingModels,
    /** getter: 读取当前激活的模型 */
    readActiveModelInfo: readonly(inUseModel),
    /** action: 初始化数据 */
    initModelStore,
    /** action: 读取模型设置 */
    getModelSetting,
    /** action: 保存模型设置 */
    setModelSetting,
    /** action: 切换真实使用模型 */
    changeActiveModel,
    /** action: 提供商连接检测 */
    checkSupplier,
    /** action: 获取提供商详情 */
    getSupplierById,
    /** action: 获取指定提供商的模型 */
    getSupplierModels,
    /** action: 获取当前模型上传附件提示信息 */
    getCurrentModelAttachUploadTips,
    /** action: 检查当前模型上传附件 */
    checkCurrentModelAttach,
    /** action: 切换模型使用状态 */
    setModelInUse,
    /** action: 获取显示的提供商名称 */
    getRealSupplierName,
    /** action: 清空所有模型 */
    clearModels,
  }
})
